package websocket

import (
	"encoding/json"
	"strconv"
	"time"

	"github.com/gorilla/websocket"
	"github.com/zeromicro/go-zero/core/logx"
)

const (
	// 允许的最大消息大小
	maxMessageSize = 512
	// 写入等待时间
	writeWait = 10 * time.Second
	// 心跳周期
	pingPeriod = (pongWait * 9) / 10
	// 读取pong等待时间
	pongWait = 60 * time.Second
)

// 客户端写入消息的循环
func (c *Client) WritePump() {
	pingTicker := time.NewTicker(pingPeriod)
	defer func() {
		pingTicker.Stop()
		c.Mu.Lock()
		if c.Conn != nil {
			c.Conn.Close()
			c.Conn = nil
		}
		c.Mu.Unlock()
	}()

	for {
		select {
		case message, ok := <-c.Send:
			c.Mu.Lock()
			if c.Conn == nil {
				c.Mu.Unlock()
				return
			}
			c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
			c.Mu.Unlock()

			if !ok {
				// 通道已关闭
				c.Mu.Lock()
				if c.Conn != nil {
					c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
				}
				c.Mu.Unlock()
				return
			}

			// 使用NextWriter发送消息
			c.Mu.Lock()
			w, err := c.Conn.NextWriter(websocket.TextMessage)
			c.Mu.Unlock()
			if err != nil {
				return
			}
			w.Write(message)

			// 发送队列中所有消息
			n := len(c.Send)
			for i := 0; i < n; i++ {
				w.Write([]byte{'\n'})
				w.Write(<-c.Send)
			}

			if err := w.Close(); err != nil {
				return
			}
		case <-pingTicker.C:
			c.Mu.Lock()
			if c.Conn == nil {
				c.Mu.Unlock()
				return
			}
			c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
			if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				c.Mu.Unlock()
				return
			}
			c.Mu.Unlock()
		}
	}
}

// 客户端读取普通房间消息的循环
func (c *Client) ReadPump(room *Room) {
	defer func() {
		room.RemoveClient(c)
	}()

	c.Conn.SetReadLimit(maxMessageSize)
	c.Conn.SetReadDeadline(time.Now().Add(pongWait))
	c.Conn.SetPongHandler(func(string) error {
		c.Conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	for {
		_, message, err := c.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				logx.Errorf("[ReadPump] 读取消息错误: %v", err)
			}
			break
		}

		var msg Message
		if err := json.Unmarshal(message, &msg); err != nil {
			logx.Errorf("[ReadPump] 消息解析失败: %v", err)
			continue
		}

		msg.UserID = c.UserID
		msg.UserName = c.UserName
		msg.Time = time.Now().Unix()

		// 根据消息类型处理
		switch msg.Type {
		case int(MessageTypeText):
			// 文本消息，直接广播
			room.Broadcast(msg)
		case int(MessageTypeReady):
			// 处理准备状态
			room.Mu.Lock()
			room.Ready[c.UserID] = msg.Content == "true"
			room.Mu.Unlock()

			// 广播准备状态变更
			readyMsg := Message{
				Type:     int(MessageTypeReady),
				UserID:   c.UserID,
				UserName: c.UserName,
				Content:  msg.Content,
				Time:     time.Now().Unix(),
			}
			room.Broadcast(readyMsg)

			// 检查是否所有用户都已准备
			allReady := true
			room.Mu.RLock()
			for _, ready := range room.Ready {
				if !ready {
					allReady = false
					break
				}
			}
			userCount := len(room.Clients)
			room.Mu.RUnlock()

			// 如果所有用户都已准备且人数大于1，可以在这里触发游戏开始
			if allReady && userCount > 1 {
				startMsg := Message{
					Type:    int(MessageTypeSystem),
					Content: "所有用户已准备，游戏即将开始...",
					Time:    time.Now().Unix(),
				}
				room.Broadcast(startMsg)
			}
		case int(MessageTypeRead):
			// 处理消息已读
			messageID := msg.Content
			room.setMessageRead(c.UserID, messageID)
		default:
			// 其他消息类型，直接广播
			room.Broadcast(msg)
		}
	}
}

// 客户端读取游戏房间消息的循环
func (c *Client) ReadPumpGame(gameRoom *GameRoom) {
	defer func() {
		gameRoom.RemoveClient(c)
	}()

	c.Conn.SetReadLimit(maxMessageSize)
	c.Conn.SetReadDeadline(time.Now().Add(pongWait))
	c.Conn.SetPongHandler(func(string) error {
		c.Conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	for {
		_, message, err := c.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				logx.Errorf("[ReadPumpGame] 读取消息错误: %v", err)
			}
			break
		}

		var msg Message
		if err := json.Unmarshal(message, &msg); err != nil {
			logx.Errorf("[ReadPumpGame] 消息解析失败: %v", err)
			continue
		}

		msg.UserID = c.UserID
		msg.UserName = c.UserName
		msg.Time = time.Now().Unix()

		// 游戏消息处理
		if msg.Type == int(MessageTypeGameOp) {
			// 处理游戏操作
			var gameOp GameOp
			if err := json.Unmarshal([]byte(msg.Content), &gameOp); err != nil {
				logx.Errorf("[ReadPumpGame] 解析游戏操作失败: %v", err)
				continue
			}

			// 异步处理游戏操作
			go gameRoom.handleGameOp(c.UserID, gameOp)
		} else {
			// 非游戏操作消息
			switch msg.Type {
			case int(MessageTypeText):
				// 文本消息，直接广播
				gameRoom.Broadcast(msg)
			case int(MessageTypeReady):
				// 处理准备状态
				gameRoom.mu.Lock()
				gameRoom.Ready[c.UserID] = msg.Content == "true"
				gameRoom.mu.Unlock()

				// 广播准备状态变更
				readyMsg := Message{
					Type:     int(MessageTypeReady),
					UserID:   c.UserID,
					UserName: c.UserName,
					Content:  msg.Content,
					Time:     time.Now().Unix(),
				}
				gameRoom.Broadcast(readyMsg)

				// 检查是否所有用户都已准备
				allReady := true
				gameRoom.mu.RLock()
				for _, ready := range gameRoom.Ready {
					if !ready {
						allReady = false
						break
					}
				}
				userCount := len(gameRoom.Clients)
				gameRoom.mu.RUnlock()

				// 如果所有用户都已准备且人数大于1，开始游戏
				if allReady && userCount > 1 && gameRoom.GameState == nil {
					gameRoom.StartGame()
				}
			case int(MessageTypeRead):
				// 处理消息已读
				_, err := strconv.Atoi(msg.Content)
				if err == nil {
					// 这里应该调用游戏房间的setMessageRead方法
				}
			default:
				// 其他消息类型，直接广播
				gameRoom.Broadcast(msg)
			}
		}
	}
}
